chore: prepare release v0.6.0#44
Conversation
…rary
Introduces the full DashboardLayout/DashboardSidebar module for apps/web
and expands @workspace/ui from ~5 to 40+ shadcn/ui components.
## Dashboard module — apps/web/modules/dashboard/
- DashboardLayout: server component reading sidebar_state cookie for
SSR-persisted collapse state; composes AuthGuard + OrganizationGuard
+ SidebarProvider + DashboardSidebar + main
- DashboardSidebar: client component with three-section nav
- Header: OrganizationSwitcher asChild with Clerk appearance tokens
for sidebar-width and collapsible-icon states
- Content: Customer Support (Conversations, Knowledge Base),
Configuration (Widget Customization, Integrations, Voice Assistant),
Account (Plans & Billing); active-route via usePathname
- Footer: UserButton showName with Clerk appearance for icon-collapse
- SidebarRail for drag-to-resize; collapsible=icon mode
## Dashboard pages — apps/web/app/(dashboard)/
Six stub pages scaffolded:
/conversations, /files, /customization, /integrations,
/plugins/vapi, /billing
## App router
- app/(dashboard)/layout.tsx: replaced inline AuthGuard +
OrganizationGuard with single DashboardLayout delegation
## UI library expansion — packages/ui
40+ shadcn/ui components added: accordion, alert-dialog, alert,
aspect-ratio, avatar, badge, breadcrumb, calendar, card, carousel,
chart, checkbox, collapsible, command, context-menu, dialog, drawer,
dropdown-menu, empty, field, hover-card, input-group, input-otp, kbd,
label, menubar, message, native-select, navigation-menu, pagination,
popover, progress, radio-group, resizable, scroll-area, select,
separator, sheet, sidebar, skeleton, slider, sonner, spinner, switch,
table, tabs, textarea, toggle-group, toggle, tooltip
New Radix UI primitives and peer deps: react-accordion,
react-alert-dialog, react-aspect-ratio, react-avatar, react-checkbox,
react-collapsible, react-context-menu, react-dialog,
react-dropdown-menu, react-hover-card, react-label, react-menubar,
react-navigation-menu, react-popover, react-progress, react-radio-group,
react-scroll-area, react-select, react-separator, react-slider,
react-switch, react-tabs, react-toggle, react-toggle-group,
react-tooltip, cmdk, date-fns, embla-carousel-react, input-otp,
react-day-picker, react-resizable-panels, recharts, sonner, vaul
Core updates: button.tsx rewritten, input.tsx updated, globals.css
expanded with sidebar/chart design tokens and Tailwind v4 layer
## Docs
- CHANGELOG.md: v0.6.0 section with full technical detail
- README.md: Dashboard Layout feature, architecture diagram updated,
design system description updated
#43) feat(web): add dashboard module, sidebar layout, and expand UI library
|
Warning Review limit reached
Next review available in: 46 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. 📝 WalkthroughWalkthroughThis PR adds a Dashboard module (sidebar client component, async server layout with cookie-persisted state, and six stub route pages), replaces the previous auth/organization guard wrapping in the dashboard root layout with delegation to the new layout, and substantially expands the ChangesDashboard Layout and Pages
Estimated code review effort: 3 (Moderate) | ~25 minutes UI Component Library Expansion
Estimated code review effort: 4 (Complex) | ~75 minutes Changelog and README Documentation
Estimated code review effort: 1 (Trivial) | ~5 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/ui/src/components/aspect-ratio.tsx (1)
1-23: 🎯 Functional Correctness | 🔴 Critical | ⚡ Quick winMissing
Reactimport will break compilation.
React.ComponentProps<"div">andReact.CSSPropertiesare used butReactis never imported in this file, unlike every sibling component (avatar.tsx, popover.tsx, hover-card.tsx allimport * as React from "react"). This will fail with "Cannot find namespace 'React'".🐛 Proposed fix
+import * as React from "react" + import { cn } from "`@workspace/ui/lib/utils`"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/aspect-ratio.tsx` around lines 1 - 23, The AspectRatio component references React.ComponentProps and React.CSSProperties without importing React, which will break compilation. Update the AspectRatio module to import React the same way as the sibling components, and keep the existing use of React.ComponentProps<"div"> and React.CSSProperties in the AspectRatio function unchanged otherwise.packages/ui/src/components/collapsible.tsx (1)
1-34: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winImport React before using
React.ComponentProps
packages/ui/src/components/collapsible.tsxusesReact.ComponentPropsbut never importsReact. Thereact-jsxsetting covers JSX, not theReacttype namespace, so this file fails typechecking until the import is added.🐛 Proposed fix
"use client" +import * as React from "react" import * as CollapsiblePrimitive from "`@radix-ui/react-collapsible`"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/collapsible.tsx` around lines 1 - 34, The Collapsible component file uses React.ComponentProps in Collapsible, CollapsibleTrigger, and CollapsibleContent without bringing React into scope, so typechecking fails. Add the missing React import at the top of the file and keep the existing component definitions unchanged so the React type namespace is available for those props types.
🧹 Nitpick comments (5)
apps/web/modules/dashboard/ui/components/dashboard-sidebar/index.tsx (1)
109-178: 📐 Maintainability & Code Quality | 🔵 TrivialExtract a reusable
SidebarNavGroupto remove duplication.The three
SidebarGroupblocks (Customer Support, Configuration, Account) are structurally identical, differing only in label and item array. Consider a small helper component takinglabelanditemsto reduce this to three calls.Example refactor
+type NavItem = { title: string; url: string; icon: React.ComponentType<{ className?: string }> } + +const SidebarNavGroup = ({ + label, + items, + isActive, +}: { + label: string + items: NavItem[] + isActive: (url: string) => boolean +}) => ( + <SidebarGroup> + <SidebarGroupLabel>{label}</SidebarGroupLabel> + <SidebarGroupContent> + <SidebarMenu> + {items.map((item) => ( + <SidebarMenuItem key={item.title}> + <SidebarMenuButton asChild isActive={isActive(item.url)} tooltip={item.title}> + <Link href={item.url}> + <item.icon className="size-4" /> + <span>{item.title}</span> + </Link> + </SidebarMenuButton> + </SidebarMenuItem> + ))} + </SidebarMenu> + </SidebarGroupContent> + </SidebarGroup> +)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/modules/dashboard/ui/components/dashboard-sidebar/index.tsx` around lines 109 - 178, The three repeated navigation sections inside SidebarContent are duplicated and should be extracted into a reusable SidebarNavGroup helper. Create a small component near dashboard-sidebar/index.tsx that takes a label and items array, and move the shared SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, and SidebarMenuItem/SidebarMenuButton rendering logic into it. Then replace the Customer Support, Configuration, and Account blocks with three calls using customerSupportItems, configurationItems, and accountItems.apps/web/modules/dashboard/ui/layouts/dashboard-layout/index.tsx (1)
20-20: 📐 Maintainability & Code Quality | 🔵 TrivialConsider an explicit
SidebarTriggerfor discoverability.The stock shadcn pattern places
<SidebarTrigger />inside<main>alongside{children}. Here, toggling relies solely onSidebarRail(a thin edge strip) or theCmd/Ctrl+Bshortcut, both less discoverable than an explicit trigger button.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/web/modules/dashboard/ui/layouts/dashboard-layout/index.tsx` at line 20, The dashboard layout’s main content currently lacks an explicit sidebar toggle, making the sidebar harder to discover. Update the DashboardLayout component to render a SidebarTrigger inside the main area alongside the children, following the standard shadcn pattern. Use the existing DashboardLayout and the main element in the layout file as the place to add the trigger so users can open the sidebar without relying only on SidebarRail or the keyboard shortcut.packages/ui/src/components/field.tsx (1)
118-129: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win
FieldTitlereusesdata-slot="field-label", colliding withFieldLabel.
FieldTitle(Line 121) is semantically distinct fromFieldLabel(Line 107) but shares the samedata-slotvalue. This is a known upstream shadcn/ui issue (tracked as shadcn-ui/ui#8387): using a shared data-slot for different components can cause styling collisions via attribute selectors (e.g.has-[[data-slot=field-label]]) and makes intent unclear. Consider using a unique value like"field-title".🩹 Proposed fix
function FieldTitle({ className, ...props }: React.ComponentProps<"div">) { return ( <div - data-slot="field-label" + data-slot="field-title" className={cn( "flex w-fit items-center gap-2 text-sm font-medium group-data-[disabled=true]/field:opacity-50", className )} {...props} /> ) }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/field.tsx` around lines 118 - 129, `FieldTitle` is using the same `data-slot="field-label"` value as `FieldLabel`, which creates a selector collision and makes the two components ambiguous. Update `FieldTitle` in `field.tsx` to use its own unique data-slot value (for example, a title-specific slot) and keep `FieldLabel` unchanged so the two components remain distinguishable by attribute selectors.packages/ui/src/components/label.tsx (1)
7-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
LabelPrimitive.Roothere
packages/ui/package.jsonalready includes@radix-ui/react-label, butpackages/ui/src/components/label.tsxstill renders a native<label>. If this component is meant to match the new Radix primitives, switch it toLabelPrimitive.Rootso it keeps the same label behavior as the rest of the set.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/label.tsx` around lines 7 - 18, The Label component still renders a native label instead of the Radix primitive. Update the Label component in label.tsx to use LabelPrimitive.Root from `@radix-ui/react-label`, keeping the existing props, className handling, and data-slot behavior intact so it matches the rest of the Radix-based UI primitives.packages/ui/src/components/tooltip.tsx (1)
21-29: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueRedundant
TooltipProvidernesting.
Tooltipalways wraps itself in a newTooltipProvider. SinceSidebarProvider(packages/ui/src/components/sidebar.tsx:131) already wraps the tree in aTooltipProvider, everySidebarMenuButtontooltip (sidebar.tsx:536-544) creates a redundant nested provider. Harmless functionally, but unnecessary re-instantiation on a hot path (menu items).🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/ui/src/components/tooltip.tsx` around lines 21 - 29, The Tooltip component currently creates a new TooltipProvider every time Tooltip renders, which causes redundant nested providers when it’s used inside SidebarProvider. Update Tooltip in tooltip.tsx so it no longer always instantiates its own provider; instead, rely on the existing TooltipProvider from the surrounding tree (such as SidebarProvider) and keep TooltipPrimitive.Root focused on rendering the tooltip itself. Use Tooltip and TooltipProvider as the key symbols when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/ui/src/components/carousel.tsx`:
- Around line 96-105: The `useEffect` in `Carousel` registers both `reInit` and
`select` handlers on `api`, but the cleanup only unsubscribes `select`, so the
`reInit` listener can leak across re-renders and unmounts. Update the cleanup in
`Carousel`’s effect to remove every listener added in the setup path, using the
same `onSelect` callback for both `api.on` subscriptions so the corresponding
`api.off` calls stay consistent.
In `@packages/ui/src/components/drawer.tsx`:
- Around line 32-46: The DrawerOverlay in drawer.tsx is using invalid Tailwind
data variants, so the open/closed animation classes will never apply. Update the
className in DrawerOverlay to use the same data attribute variant syntax
supported elsewhere in this codebase, or define custom variants that match the
attribute Vaul sets on DrawerPrimitive.Overlay. Keep the change localized to
DrawerOverlay and verify the fade/animate classes are driven by the actual data
attribute names.
In `@packages/ui/src/components/empty.tsx`:
- Around line 71-82: EmptyDescription is typed as paragraph props but renders a
div, so align the component’s prop type with its actual element or change the
element to match the semantic description text usage. Update the
EmptyDescription component signature and its JSX in the empty.tsx component so
the rendered tag and React.ComponentProps type are consistent, and keep the
data-slot/className behavior unchanged.
- Around line 1-4: The empty component module uses React.ComponentProps without
importing React, so add the React namespace import at the top of empty.tsx.
Update the imports in the component file that defines the Empty-related types
and props so React.ComponentProps resolves correctly, alongside the existing cva
and cn imports.
In `@packages/ui/src/components/menubar.tsx`:
- Around line 219-241: The MenubarSubTrigger component is using outline-none,
which drops the forced-colors/high-contrast focus ring; update the className in
MenubarSubTrigger to use outline-hidden instead, matching the other menubar item
components and preserving the same focus behavior.
---
Outside diff comments:
In `@packages/ui/src/components/aspect-ratio.tsx`:
- Around line 1-23: The AspectRatio component references React.ComponentProps
and React.CSSProperties without importing React, which will break compilation.
Update the AspectRatio module to import React the same way as the sibling
components, and keep the existing use of React.ComponentProps<"div"> and
React.CSSProperties in the AspectRatio function unchanged otherwise.
In `@packages/ui/src/components/collapsible.tsx`:
- Around line 1-34: The Collapsible component file uses React.ComponentProps in
Collapsible, CollapsibleTrigger, and CollapsibleContent without bringing React
into scope, so typechecking fails. Add the missing React import at the top of
the file and keep the existing component definitions unchanged so the React type
namespace is available for those props types.
---
Nitpick comments:
In `@apps/web/modules/dashboard/ui/components/dashboard-sidebar/index.tsx`:
- Around line 109-178: The three repeated navigation sections inside
SidebarContent are duplicated and should be extracted into a reusable
SidebarNavGroup helper. Create a small component near
dashboard-sidebar/index.tsx that takes a label and items array, and move the
shared SidebarGroup, SidebarGroupLabel, SidebarGroupContent, SidebarMenu, and
SidebarMenuItem/SidebarMenuButton rendering logic into it. Then replace the
Customer Support, Configuration, and Account blocks with three calls using
customerSupportItems, configurationItems, and accountItems.
In `@apps/web/modules/dashboard/ui/layouts/dashboard-layout/index.tsx`:
- Line 20: The dashboard layout’s main content currently lacks an explicit
sidebar toggle, making the sidebar harder to discover. Update the
DashboardLayout component to render a SidebarTrigger inside the main area
alongside the children, following the standard shadcn pattern. Use the existing
DashboardLayout and the main element in the layout file as the place to add the
trigger so users can open the sidebar without relying only on SidebarRail or the
keyboard shortcut.
In `@packages/ui/src/components/field.tsx`:
- Around line 118-129: `FieldTitle` is using the same `data-slot="field-label"`
value as `FieldLabel`, which creates a selector collision and makes the two
components ambiguous. Update `FieldTitle` in `field.tsx` to use its own unique
data-slot value (for example, a title-specific slot) and keep `FieldLabel`
unchanged so the two components remain distinguishable by attribute selectors.
In `@packages/ui/src/components/label.tsx`:
- Around line 7-18: The Label component still renders a native label instead of
the Radix primitive. Update the Label component in label.tsx to use
LabelPrimitive.Root from `@radix-ui/react-label`, keeping the existing props,
className handling, and data-slot behavior intact so it matches the rest of the
Radix-based UI primitives.
In `@packages/ui/src/components/tooltip.tsx`:
- Around line 21-29: The Tooltip component currently creates a new
TooltipProvider every time Tooltip renders, which causes redundant nested
providers when it’s used inside SidebarProvider. Update Tooltip in tooltip.tsx
so it no longer always instantiates its own provider; instead, rely on the
existing TooltipProvider from the surrounding tree (such as SidebarProvider) and
keep TooltipPrimitive.Root focused on rendering the tooltip itself. Use Tooltip
and TooltipProvider as the key symbols when making the change.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 79bbc63d-eeb7-4dfb-a5c3-d605b68b681f
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (67)
CHANGELOG.mdREADME.mdapps/web/app/(dashboard)/billing/page.tsxapps/web/app/(dashboard)/conversations/page.tsxapps/web/app/(dashboard)/customization/page.tsxapps/web/app/(dashboard)/files/page.tsxapps/web/app/(dashboard)/integrations/page.tsxapps/web/app/(dashboard)/layout.tsxapps/web/app/(dashboard)/plugins/vapi/page.tsxapps/web/modules/dashboard/ui/components/dashboard-sidebar/index.tsxapps/web/modules/dashboard/ui/layouts/dashboard-layout/index.tsxapps/web/package.jsonpackages/ui/package.jsonpackages/ui/src/components/accordion.tsxpackages/ui/src/components/alert-dialog.tsxpackages/ui/src/components/alert.tsxpackages/ui/src/components/aspect-ratio.tsxpackages/ui/src/components/avatar.tsxpackages/ui/src/components/badge.tsxpackages/ui/src/components/breadcrumb.tsxpackages/ui/src/components/button.tsxpackages/ui/src/components/calendar.tsxpackages/ui/src/components/card.tsxpackages/ui/src/components/carousel.tsxpackages/ui/src/components/chart.tsxpackages/ui/src/components/checkbox.tsxpackages/ui/src/components/collapsible.tsxpackages/ui/src/components/command.tsxpackages/ui/src/components/context-menu.tsxpackages/ui/src/components/dialog.tsxpackages/ui/src/components/drawer.tsxpackages/ui/src/components/dropdown-menu.tsxpackages/ui/src/components/empty.tsxpackages/ui/src/components/field.tsxpackages/ui/src/components/hover-card.tsxpackages/ui/src/components/input-group.tsxpackages/ui/src/components/input-otp.tsxpackages/ui/src/components/input.tsxpackages/ui/src/components/kbd.tsxpackages/ui/src/components/label.tsxpackages/ui/src/components/menubar.tsxpackages/ui/src/components/message.tsxpackages/ui/src/components/native-select.tsxpackages/ui/src/components/navigation-menu.tsxpackages/ui/src/components/pagination.tsxpackages/ui/src/components/popover.tsxpackages/ui/src/components/progress.tsxpackages/ui/src/components/radio-group.tsxpackages/ui/src/components/resizable.tsxpackages/ui/src/components/scroll-area.tsxpackages/ui/src/components/select.tsxpackages/ui/src/components/separator.tsxpackages/ui/src/components/sheet.tsxpackages/ui/src/components/sidebar.tsxpackages/ui/src/components/skeleton.tsxpackages/ui/src/components/slider.tsxpackages/ui/src/components/sonner.tsxpackages/ui/src/components/spinner.tsxpackages/ui/src/components/switch.tsxpackages/ui/src/components/table.tsxpackages/ui/src/components/tabs.tsxpackages/ui/src/components/textarea.tsxpackages/ui/src/components/toggle-group.tsxpackages/ui/src/components/toggle.tsxpackages/ui/src/components/tooltip.tsxpackages/ui/src/hooks/use-mobile.tspackages/ui/src/styles/globals.css
| React.useEffect(() => { | ||
| if (!api) return | ||
| onSelect(api) | ||
| api.on("reInit", onSelect) | ||
| api.on("select", onSelect) | ||
|
|
||
| return () => { | ||
| api?.off("select", onSelect) | ||
| } | ||
| }, [api, onSelect]) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
Missing cleanup for reInit listener.
Both "reInit" and "select" listeners are registered, but only "select" is removed in the cleanup function, leaking the reInit listener on every effect re-run/unmount.
🔧 Proposed fix
return () => {
+ api?.off("reInit", onSelect)
api?.off("select", onSelect)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| React.useEffect(() => { | |
| if (!api) return | |
| onSelect(api) | |
| api.on("reInit", onSelect) | |
| api.on("select", onSelect) | |
| return () => { | |
| api?.off("select", onSelect) | |
| } | |
| }, [api, onSelect]) | |
| React.useEffect(() => { | |
| if (!api) return | |
| onSelect(api) | |
| api.on("reInit", onSelect) | |
| api.on("select", onSelect) | |
| return () => { | |
| api?.off("reInit", onSelect) | |
| api?.off("select", onSelect) | |
| } | |
| }, [api, onSelect]) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/carousel.tsx` around lines 96 - 105, The
`useEffect` in `Carousel` registers both `reInit` and `select` handlers on
`api`, but the cleanup only unsubscribes `select`, so the `reInit` listener can
leak across re-renders and unmounts. Update the cleanup in `Carousel`’s effect
to remove every listener added in the setup path, using the same `onSelect`
callback for both `api.on` subscriptions so the corresponding `api.off` calls
stay consistent.
| function DrawerOverlay({ | ||
| className, | ||
| ...props | ||
| }: React.ComponentProps<typeof DrawerPrimitive.Overlay>) { | ||
| return ( | ||
| <DrawerPrimitive.Overlay | ||
| data-slot="drawer-overlay" | ||
| className={cn( | ||
| "fixed inset-0 z-50 bg-black/10 data-closed:animate-out data-closed:fade-out-0 data-open:animate-in data-open:fade-in-0 supports-backdrop-filter:backdrop-blur-xs", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does vaul Drawer.Overlay component set data-state attribute like Radix Dialog?
💡 Result:
The Vaul Drawer.Overlay component does not use the standard data-state attribute (i.e., data-state="open" or data-state="closed") found in Radix UI primitives [1][2]. While Vaul is built on top of Radix UI's Dialog primitive [3], the Vaul implementation overrides the behavior of the overlay for its own custom state management [1][2]. Instead of the standard Radix data-state attribute, the Drawer.Overlay component uses a set of custom data attributes, including [1]: data-vaul-overlay="" data-vaul-snap-points data-vaul-snap-points-overlay data-vaul-animate Because Vaul does not expose the standard Radix data-state on its overlay, developers who wish to use CSS selectors like [data-state=open] to style the overlay must either apply those attributes manually or use a wrapper that adds them based on the drawer's state [4]. The absence of this native attribute has been a subject of discussion within the Vaul community, with some users noting the desire for standard data attributes to simplify integration with utility-first CSS frameworks like Tailwind CSS [5].
Citations:
- 1: https://github.com/emilkowalski/vaul/blob/main/src/index.tsx
- 2: https://cdn.jsdelivr.net/npm/@adophilus/vaul@0.9.2/src/index.tsx
- 3: https://emilkowal.ski/ui/building-a-drawer-component
- 4: https://cdn.jsdelivr.net/npm/@vuer-ai/vuer-uikit@0.0.120/src/ui/drawer.tsx
- 5: Should vaul use standard data attribute? emilkowalski/vaul#369
🏁 Script executed:
#!/bin/bash
set -euo pipefail
git ls-files packages/ui/src/components/drawer.tsx packages/ui/package.json package.json
echo '--- drawer.tsx ---'
cat -n packages/ui/src/components/drawer.tsx
echo '--- package.json (ui) ---'
cat -n packages/ui/package.json
echo '--- root package.json ---'
cat -n package.jsonRepository: RISHII7/echo
Length of output: 9416
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- search data-open / data-closed usage ---'
rg -n --hidden --glob '!**/node_modules/**' 'data-(open|closed):|data-\[state=' packages root package.json package.json tailwind.config.* postcss.config.* 2>/dev/null || true
echo '--- locate Tailwind config / css entrypoints ---'
git ls-files | rg 'tailwind|postcss|globals\.css|\.css$'
echo '--- inspect relevant styling files ---'
for f in $(git ls-files | rg 'tailwind|postcss|globals\.css|\.css$'); do
echo "--- $f ---"
wc -l "$f"
doneRepository: RISHII7/echo
Length of output: 17543
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo '--- packages/ui/postcss.config.mjs ---'
cat -n packages/ui/postcss.config.mjs
echo '--- packages/ui/src/styles/globals.css ---'
sed -n '1,220p' packages/ui/src/styles/globals.css
echo '--- search for custom data-open/data-closed variant definitions ---'
rg -n --hidden --glob '!**/node_modules/**' 'data-open|data-closed|addVariant|`@custom-variant`|custom-variant' packages/ui appsRepository: RISHII7/echo
Length of output: 5139
🌐 Web query:
vaul 1.1.2 Drawer.Overlay data-state data-vaul-overlay source
💡 Result:
In Vaul 1.1.2, Drawer.Overlay is a component that renders a backdrop layer covering the inert portion of the view when the drawer is open [1][2]. It is intended to be used within the Drawer.Portal component [3][2]. Styling hooks for the overlay are exposed via specific data attributes that allow for CSS targeting [3]. The primary attribute is data-vaul-overlay, which is applied to the overlay element to facilitate custom styling [3]. Historically, older versions or internal implementations used attributes like vaul-overlay or vaul-snap-points-overlay [4][5], but standard usage in recent versions of Vaul relies on data-vaul-* attributes for CSS selectors [3]. These attributes are automatically managed by the drawer state (e.g., open/closed status or snap point transitions) [3][4]. If you are attempting to style the overlay, you should target the [data-vaul-overlay] attribute in your CSS [3]. Example CSS: [data-vaul-overlay] { background: rgba(0, 0, 0, 0.4); } Example usage in JSX: <Drawer.Portal> <Drawer.Overlay /> <Drawer.Content> {/* Drawer Content */} </Drawer.Content> </Drawer.Portal>
Citations:
- 1: https://vaul.emilkowal.ski/api
- 2: https://registry.npmjs.org/vaul
- 3: https://github.com/nunesunil/base-ui-vaul
- 4: https://cdn.jsdelivr.net/npm/@adophilus/vaul@0.9.2/src/index.tsx
- 5: Snap point overlay emilkowalski/vaul#535
🌐 Web query:
vaul Drawer.Overlay source data-vaul-overlay data-state site:github.com/emilkowalski/vaul
💡 Result:
In Vaul, the Drawer.Overlay component and other drawer elements utilize standard data attributes to reflect their current state and configuration, which facilitates styling via CSS or utility frameworks like Tailwind CSS [1][2]. The data-state attribute is used to track the visibility and animation status of the drawer components. It typically takes the values "open" or "closed" [3]. Regarding data-vaul- attributes: - Following updates to align with standard data attribute practices, internal Vaul attributes were renamed from custom prefixed attributes (like vaul-drawer) to prefixed data attributes (e.g., data-vaul-drawer, data-vaul-drawer-direction) [1][2]. - These attributes allow you to target specific drawer states and directions in your CSS [1]. For example, you can use data-[vaul-drawer-direction=right] to apply conditional styling based on the drawer's direction [1]. - The Drawer.Overlay component is rendered internally and can be styled directly, although it is often managed as part of the Drawer.Content structure [4][5]. These data attributes are designed to be consistent with conventions used by other UI libraries (such as Radix UI), making them predictable for developers using Tailwind CSS data attribute selectors [1][6].
Citations:
- 1: Should vaul use standard data attribute? emilkowalski/vaul#369
- 2: ISSUE-369: Make vaul use data- attr for Content direction emilkowalski/vaul#404
- 3: Remove default animations for custom animations emilkowalski/vaul#87
- 4: Styling DrawerOverlay without overlay prop emilkowalski/vaul#606
- 5: feat: export useDrawerContext emilkowalski/vaul#467
- 6: change
vaul-drawer-wrapperto data attribute emilkowalski/vaul#131
🌐 Web query:
Tailwind CSS data-open variant data-closed variant support
💡 Result:
Tailwind CSS provides robust, built-in support for styling elements based on data attributes through its data-* variant system [1][2][3]. Arbitrary Data Attribute Variants You can use the data-* variant syntax to target any data attribute dynamically without additional configuration [1][3]. The syntax follows the pattern data-[attribute=value] [1][3]. Examples: - To target an element where data-open="true": class="data-[open=true]:bg-blue-500" - To target an element where data-state="closed": class="data-[state=closed]:hidden" This approach also supports group and peer modifiers to style child or sibling elements based on a parent's or peer's data attribute state [1][4]: - Group example: class="group-data-[open=true]:block" - Peer example: class="peer-data-[state=closed]:opacity-50" Configuration (Optional) If you frequently use specific data attributes, you can define shortcuts in your tailwind.config.js (or equivalent configuration) under the theme.data section [1][3]. This allows for cleaner utility classes [1]: // tailwind.config.js example module.exports = { theme: { data: { open: 'state~="open"', closed: 'state~="closed"', }, }, } // Resulting usage:
@custom-variant directive in your CSS, which provides more control for complex state selectors [5]: @custom-variant open (&[data-state="open"]); @custom-variant closed (&[data-state="closed"]); /* Usage */ Citations:
- 1: Add dynamic
data-*variant tailwindlabs/tailwindcss#9559 - 2: https://llmbestpractices.com/frontend/tailwind-variants
- 3: https://tailwindlabs-tailwindcss.mintlify.app/customization/variants
- 4: https://www.protailwind.com/tips/use-the-new-data-attribute-variants-to-style-elements-based-on-a-specific-data-attribute-value
- 5: https://tailwindlabs-tailwindcss.mintlify.app/advanced/custom-variants
Use a valid Tailwind data variant here data-open: / data-closed: aren’t defined in this setup, so the overlay fade classes won’t match. Switch to the data-[...] form used elsewhere here, or add custom variants for the attribute Vaul exposes.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/drawer.tsx` around lines 32 - 46, The
DrawerOverlay in drawer.tsx is using invalid Tailwind data variants, so the
open/closed animation classes will never apply. Update the className in
DrawerOverlay to use the same data attribute variant syntax supported elsewhere
in this codebase, or define custom variants that match the attribute Vaul sets
on DrawerPrimitive.Overlay. Keep the change localized to DrawerOverlay and
verify the fade/animate classes are driven by the actual data attribute names.
| import { cva, type VariantProps } from "class-variance-authority" | ||
|
|
||
| import { cn } from "@workspace/ui/lib/utils" | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file ==\n'
cat -n packages/ui/src/components/empty.tsx
printf '\n== tsconfig candidates ==\n'
git ls-files '*/tsconfig*.json' 'tsconfig*.json'
printf '\n== search for React.ComponentProps usage and import style ==\n'
rg -n 'React\.ComponentProps|import \* as React from "react"|import type \* as React from "react"|type ComponentProps' packages/ui/src/components -g '*.tsx' -g '*.ts'Repository: RISHII7/echo
Length of output: 35036
Import React for React.ComponentProps
packages/ui/src/components/empty.tsx uses React.ComponentProps in several places, but React is never imported. Add import * as React from "react" at the top.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/empty.tsx` around lines 1 - 4, The empty component
module uses React.ComponentProps without importing React, so add the React
namespace import at the top of empty.tsx. Update the imports in the component
file that defines the Empty-related types and props so React.ComponentProps
resolves correctly, alongside the existing cva and cn imports.
| function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { | ||
| return ( | ||
| <div | ||
| data-slot="empty-description" | ||
| className={cn( | ||
| "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", | ||
| className | ||
| )} | ||
| {...props} | ||
| /> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
EmptyDescription typed as <p> props but renders a <div>.
The prop type promises React.ComponentProps<"p"> while the actual element is a <div>, allowing paragraph-only attributes to be spread onto a div and diverging from the typical semantic <p> used for description text.
🛠️ Proposed fix
-function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
+function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) {
return (
- <div
+ <p
data-slot="empty-description"
className={cn(
"text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary",
className
)}
{...props}
- />
+ />
)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { | |
| return ( | |
| <div | |
| data-slot="empty-description" | |
| className={cn( | |
| "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", | |
| className | |
| )} | |
| {...props} | |
| /> | |
| ) | |
| } | |
| function EmptyDescription({ className, ...props }: React.ComponentProps<"p">) { | |
| return ( | |
| <p | |
| data-slot="empty-description" | |
| className={cn( | |
| "text-sm/relaxed text-muted-foreground [&>a]:underline [&>a]:underline-offset-4 [&>a:hover]:text-primary", | |
| className | |
| )} | |
| {...props} | |
| /> | |
| ) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/empty.tsx` around lines 71 - 82, EmptyDescription
is typed as paragraph props but renders a div, so align the component’s prop
type with its actual element or change the element to match the semantic
description text usage. Update the EmptyDescription component signature and its
JSX in the empty.tsx component so the rendered tag and React.ComponentProps type
are consistent, and keep the data-slot/className behavior unchanged.
| function MenubarSubTrigger({ | ||
| className, | ||
| inset, | ||
| children, | ||
| ...props | ||
| }: React.ComponentProps<typeof MenubarPrimitive.SubTrigger> & { | ||
| inset?: boolean | ||
| }) { | ||
| return ( | ||
| <MenubarPrimitive.SubTrigger | ||
| data-slot="menubar-sub-trigger" | ||
| data-inset={inset} | ||
| className={cn( | ||
| "flex cursor-default items-center rounded-sm px-2 py-1.5 text-sm outline-none select-none focus:bg-accent focus:text-accent-foreground data-inset:pl-8 data-[state=open]:bg-accent data-[state=open]:text-accent-foreground", | ||
| className | ||
| )} | ||
| {...props} | ||
| > | ||
| {children} | ||
| <ChevronRightIcon className="ml-auto h-4 w-4" /> | ||
| </MenubarPrimitive.SubTrigger> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'FILE: packages/ui/src/components/menubar.tsx\n'
nl -ba packages/ui/src/components/menubar.tsx | sed -n '45,260p'
printf '\nUSAGE OF outline-hidden / outline-none IN FILE:\n'
rg -n 'outline-(hidden|none)' packages/ui/src/components/menubar.tsx
printf '\nRELEVANT PACKAGE INFO:\n'
for f in package.json packages/ui/package.json pnpm-workspace.yaml; do
if [ -f "$f" ]; then
echo "--- $f"
sed -n '1,220p' "$f" | rg -n '"tailwindcss"|outline-hidden|outline-none|version|dependencies|devDependencies' -n -C 1 || true
fi
done
printf '\nTAILWIND REFERENCES IN REPO:\n'
rg -n '"tailwindcss"|outline-hidden|outline-none' . --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' | sed -n '1,200p'Repository: RISHII7/echo
Length of output: 235
🌐 Web query:
Tailwind CSS v4 outline-none outline-hidden forced-colors high contrast accessibility documentation
💡 Result:
In Tailwind CSS v4, the handling of outline utilities has been updated to improve clarity and accessibility compliance [1][2]. Key changes include: 1. Renaming and New Utilities: The former outline-none utility (which provided an invisible 2px outline to preserve accessibility in forced-colors mode) has been renamed to outline-hidden [1][2]. A new, literal outline-none utility has been introduced that simply sets outline-style: none [1][2]. 2. outline-hidden and Accessibility: The outline-hidden utility is preferred when you need to hide an outline while maintaining accessibility standards [3]. It is specifically designed to ensure that focus indicators remain visible when the user has forced-colors mode (high contrast mode) enabled in their browser or operating system [2][3]. 3. Forced-Colors Support: Tailwind CSS v4 includes native support for the forced-colors media feature [4][5]. Beyond outline-hidden, you can use the forced-color-adjust utility (mapped to the CSS forced-color-adjust property) to control how elements behave in high contrast environments [6][5]. When building for accessibility, it is recommended to use outline-hidden if your intention is to replace the default browser focus ring with a custom design (e.g., using box shadows), as this ensures the focus remains perceivable for all users [2]. Use the new outline-none with caution, as it will remove the focus indicator entirely, which can lead to accessibility issues unless you have implemented an alternative, highly visible focus state [2].
Citations:
- 1: https://tailwindcss.com/docs/upgrade-guide
- 2: Replace
outline-nonewithoutline-hidden, add newoutline-nonetailwindlabs/tailwindcss#14926 - 3:
outline-hiddenonly applies transparent outline whenforced-colorsis active? tailwindlabs/tailwindcss#16926 - 4: https://v3.tailwindcss.com/docs/v4-beta
- 5: Add
forced-color-adjustutilities tailwindlabs/tailwindcss#11931 - 6: https://tailwindcss.com/docs/forced-color-adjust
🌐 Web query:
shadcn-ui/ui issue 10101 MenubarSubTrigger outline-none outline-hidden
💡 Result:
The issue #10101 in the shadcn-ui/ui repository concerns an accessibility inconsistency where components, including MenubarSubTrigger, were using outline-none in the v4 registry instead of outline-hidden [1][2]. In Tailwind CSS and web development, outline-none removes the focus outline entirely, which can negatively impact accessibility by making components difficult or impossible to identify for keyboard users and those utilizing assistive technologies [2]. In contrast, outline-hidden is designed to be used in conjunction with high-contrast/forced-colors modes, ensuring that the focus indicator remains visible and accessible in those environments while appearing hidden in others [2]. This issue was addressed by pull request #10102, which proposed replacing all occurrences of outline-none with outline-hidden across the v4 registry files to ensure consistency and improve accessibility [2]. If you are experiencing this issue, you can resolve it by manually updating the className properties in your local copy of the component to use outline-hidden [2].
Citations:
- 1: [bug]: v4 registry incomplete outline-none to outline-hidden migration (19 components) shadcn-ui/ui#10101
- 2: fix: update outline styles to outline-hidden in v4 registry shadcn-ui/ui#10102
🏁 Script executed:
# No-op placeholder to keep investigation focused on the retrieved outputs.Repository: RISHII7/echo
Length of output: 150
Use outline-hidden for MenubarSubTrigger. outline-none removes the forced-colors/high-contrast focus ring in Tailwind v4; outline-hidden matches the other menubar items.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/components/menubar.tsx` around lines 219 - 241, The
MenubarSubTrigger component is using outline-none, which drops the
forced-colors/high-contrast focus ring; update the className in
MenubarSubTrigger to use outline-hidden instead, matching the other menubar item
components and preserving the same focus behavior.
Release v0.6.0 — Dashboard Module, Sidebar Layout, and UI Library Expansion
What's in this release
Dashboard module —
apps/web/modules/dashboard/DashboardLayoutserver component: readssidebar_statecookie, composesAuthGuard+OrganizationGuard+SidebarProvider+DashboardSidebarDashboardSidebarclient component:OrganizationSwitcherheader, three nav groups (Customer Support, Configuration, Account),UserButtonfooter,SidebarRail,collapsible="icon"modeDashboard pages scaffolded
Six stub routes:
/conversations,/files,/customization,/integrations,/plugins/vapi,/billingUI library expansion —
packages/ui40+ new shadcn/ui components, 25 new Radix UI primitives, updated
button.tsx/input.tsx/globals.cssApp router refactor
app/(dashboard)/layout.tsxnow delegates to<DashboardLayout>— guards moved into the moduleCI
All checks passed on PR #43 (feature → develop):
Format Check ✅ · Lint ✅ · Type Check ✅ · Build ✅ · Validate PR Title ✅ · CodeQL ✅
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes